#include "stdafx.h"
#include "VBoostApp.h"
#include "UnkHook.h"
#include "FixedMemMgr.h"
#include "blinddel.h"

HRESULT CVBoostApp::CreateInstance(CVBoostApp** ppApp)
{
    if (NULL == (*ppApp = new CVBoostApp()))
    {
        return E_OUTOFMEMORY;
    }
    (*ppApp)->AddRef();
    return NOERROR;
}

STDMETHODIMP
CVBoostApp::HookQI(IUnknown *pUnk, IQIHook* pQIHook, UnkHookFlags uhFlags, UnknownHook** ppOwner)
{
    if (pUnk == NULL || pQIHook == NULL)
    {
        // Note: we can't check ppOwner for NULL because VB will always
        // send us a pointer to the out parameter, even if it is to a temp.
        return 0x800A005B; // Object variable not set
    }
    // Note: We regenerate the typelib with mktyplib to force the typelib
    // to contain stdole.IUnknown instead of VT_UNKNOWN.  This means that,
    // when VB calls this, pUnk will always be the controlling IUnknown.
    // Otherwise, we would have to QI for IID_IUnknown here.
    return CUnkHook::CreateInstance(pUnk, (IQIARHook*)pQIHook, uhFlags, false, ppOwner);
}

STDMETHODIMP
CVBoostApp::HookQIAR(IUnknown *pUnk, IQIARHook* pQIARHook, UnkHookFlags uhFlags, UnknownHook** ppOwner)
{
    if (pUnk == NULL || pQIARHook == NULL)
    {
        // Note: we can't check ppOwner for NULL because VB will always
        // send us a pointer to the out parameter, even if it is to a temp.
        return 0x800A005B; // Object variable not set
    }
    // Note: We regenerate the typelib with mktyplib to force the typelib
    // to contain stdole.IUnknown instead of VT_UNKNOWN.  This means that,
    // when VB calls this, pUnk will always be the controlling IUnknown.
    // Otherwise, we would have to QI for IID_IUnknown here.
    return CUnkHook::CreateInstance(pUnk, pQIARHook, uhFlags, true, ppOwner);
}

// This looks bizarre, but the IDL definition takes a true VT_UNKNOWN
// as the input and returns a stdole.IUnknown.  When assigning
// to a stdole.IUnkown parameter that requires the true pointer instead
// of the controlling IUnknown, wrap the assignment with this function.
STDMETHODIMP
CVBoostApp::SafeUnknown(IUnknown* pUnknown, IUnknown** retVal)
{
    if (*retVal = pUnknown)
    {
        pUnknown->AddRef();
    }
    return NOERROR;
}

// Blind delegator creation
STDMETHODIMP
CVBoostApp::CreateDelegator(IUnknown* punkOuter, IUnknown* punkDelegatee, VBGUIDPtr piid, BlindDelegatorPtr pEmbedded, long pfnDestroyEmbedded, IUnknown** ppvObj)
{
    if (punkOuter == NULL ||
        punkDelegatee == NULL ||
        (pEmbedded == NULL && pfnDestroyEmbedded != NULL))
    {
        *ppvObj = NULL;
        return E_INVALIDARG;
    }
    return _BlindDelegator::CreateDelegator(punkOuter, punkDelegatee, (IID*)piid, (_BlindDelegator*)pEmbedded, (DestroyBlindDelegator)pfnDestroyEmbedded, ppvObj);
}

// Blind delegator function pointer access
STDMETHODIMP
CVBoostApp::BlindFunctionPointer(long FunctionNumber, long *retVal)
{
    // Test special value to get the whole vtable
    if (FunctionNumber == -1)
    {
        *retVal = (long)&g_bdvtbl;
        return NOERROR;
    }
    else
    {
        return ((ULONG)FunctionNumber > MAX_VTABLE_ENTRIES) ?
                 DISP_E_BADINDEX :
                 *retVal = (long)g_bdvtbl[FunctionNumber], NOERROR;
    }
}

// Aggregate an existing unknown.  This is similar to HookUnknown, but a QIHook
// implementation is provided based on the passed in data.
STDMETHODIMP
CVBoostApp::AggregateUnknown(IUnknown* pUnk, SAFEARRAY** pData, SAFEARRAY** pIIDs, UnknownHook** ppOwner)
{
    return CUnkHookAggregate::CreateInstance(pUnk, *pData, *pIIDs, ppOwner);
}

// Helper function to turn a weak reference into a strong reference
// with the most efficient code possible.  StrongRef is an [out] parameter
// so we don't have to worry about Releasing it, just setting the value.  You
// either pass in a WeakRef (or variable) or a normal reference as the src.
STDMETHODIMP_(void)
CVBoostApp::AssignAddRef(void* pDst, void* pSrc)
{
    if (*(IUnknown**)pDst = *(IUnknown**)pSrc)
    {
        (*(IUnknown**)pDst)->AddRef();
    }
}

//STDMETHODIMP_(void)
void _declspec(naked) STDMETHODCALLTYPE
CVBoostApp::AssignSwap(void *pLeft, void *pRight)
{
    // Optimized asm code
    _asm
    {
        mov eax, [esp + 8]
        mov edx, [esp + 12]
        mov ecx, [eax]
        xchg ecx, [edx]
        mov [eax], ecx
        ret 12
    }
    //C++ code, asm beats this by 3x
    //void* tmp = *(void**)pLeft;
    //*(void**)pLeft = *(void**)pRight;
    //*(void**)pRight = tmp;
}

STDMETHODIMP
CVBoostApp::CreateFixedSizeMemoryManager(long ElementSize, long ElementsPerBlock, VARIANT_BOOL fCompactible, FixedSizeMemoryManager** retVal)
{
    return fCompactible ? 
             CFixedMemMgrCompact::CreateInstance(ElementSize, ElementsPerBlock, retVal) : 
             CFixedMemMgr::CreateInstance(ElementSize, ElementsPerBlock, retVal);
}

// Like AggregateUnknown, except creates a new object instead of
// hooking to aggregate on top of an existing one.
STDMETHODIMP
CVBoostApp::CreateAggregate(SAFEARRAY** pData, SAFEARRAY** pIIDs, long pOwner, IUnknown** ppvObj)
{
    return CAggregateObject::CreateInstance(*pData, *pIIDs, (IUnknown**)pOwner, ppvObj);
}

// VBoost Application Object ClassFactory
HRESULT CVBoostAppCF::Create(IUnknown** ppUnk)
{
    return CVBoostApp::CreateInstance((CVBoostApp**)ppUnk);
}
